package com.comut.crud.jms;

import com.comut.crud.util.Util;
import com.comut.lib.connection.MongoDBConnectionManager;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 * Issue: IFL-1572. Gets info for trailers
 *
 * @author Vitor Borrego
 */
@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destination",
            propertyValue = "queue/TrailerInfoQueue"),
    @ActivationConfigProperty(propertyName = "acknowledgeMode",
            propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue")
})
public class TrailerInfoMDB implements MessageListener {

    private MongoDBConnectionManager mongoDB;
    private Mongo mongoConnection;
    private DB mongoDatabase;
    private DBCollection hisTrailer;
    private Connection jmsConnection;
    private Session jmsSession;
    private MessageProducer producer;
    private DBCollection mobileInfo;

    /**
     * Issue: IFL-1572
     */
    @PostConstruct
    private void postConstruct() {
        try {
            this.checkMongoDBConnection();
            this.connectJMS();

        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Issue: IFL-1572
     */
    @PreDestroy
    private void preDestroy() {
        try {
            this.mongoDB.freeConnection(this.mongoConnection, this.mongoDatabase);
            this.jmsSession.close();
            this.jmsConnection.close();
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Issue: IFL-1572
     *
     * @param msg
     */
    public void onMessage(Message msg) {
        if (msg instanceof TextMessage) {
            this.checkMongoDBConnection();
            TextMessage textMessage = (TextMessage) msg;

            try {
                int trailerId = textMessage.getIntProperty("trailerId");
                String taskId = textMessage.getStringProperty("taskId");
                //  db.his_trailer.find({trailer:48112}).sort({from:-1}).limit(1).pretty()
                TextMessage replyMessage = jmsSession.createTextMessage("Reply");
                replyMessage.setStringProperty("taskId", taskId);
                replyMessage.setIntProperty("trailerId", trailerId);
                this.getDataFromHisTrailerCollection(trailerId, replyMessage);
                this.getDataFromMobileInfoCollection(replyMessage);

                this.producer.send(replyMessage, DeliveryMode.NON_PERSISTENT, 9, 10000);
            } catch (Exception ex) {
                Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Issue: IFL-1572
     *
     * @param trailerId
     * @param replyMessage
     * @throws NumberFormatException
     * @throws JMSException
     */
    private void getDataFromHisTrailerCollection(int trailerId, TextMessage replyMessage) throws NumberFormatException, JMSException {
        BasicDBObject queryMongo = new BasicDBObject();
        queryMongo.put("trailer", trailerId);
        DBCursor cursor = this.hisTrailer.find(queryMongo);
        cursor.limit(1);
        BasicDBObject sortExpression = new BasicDBObject();
        sortExpression.put("from", -1); // most recent from
        cursor.sort(sortExpression);
        // Logger.getLogger(this.getClass().getName()).log(Level.INFO, String.format("%s", cursor.toString()));
        List<DBObject> resultsTrailer = cursor.toArray();

        replyMessage.setLongProperty("from", -1);
        replyMessage.setLongProperty("to", -1);
        replyMessage.setIntProperty("vehicleId", 0);

        for (DBObject result : resultsTrailer) {
            if (result.containsField("from")) {
                replyMessage.setLongProperty("from", ((Date) result.get("from")).getTime());
            }
            if (result.containsField("to")) {
                replyMessage.setLongProperty("to", ((Date) result.get("to")).getTime());
            } else {
                replyMessage.setLongProperty("to", -1);
            }

            if (result.containsField("mid")) {
                replyMessage.setIntProperty("vehicleId", Integer.parseInt(result.get("mid").toString()));
            }
        }
    }

    /**
     * Issue: IFL-1572
     *
     * @param replyMessage
     */
    private void getDataFromMobileInfoCollection(TextMessage replyMessage) {
        try {
            int vehicleId = replyMessage.getIntProperty("vehicleId");
            BasicDBObject fields = new BasicDBObject();
            fields.put("_id", 1);
            fields.put("pos.gsp", 1); //int
            fields.put("pos.hdg", 1); //int
            fields.put("pos.loc.lat", 1); //double
            fields.put("pos.loc.lon", 1); //double 

            BasicDBObject queryMongo = new BasicDBObject();
            queryMongo.put("mid", vehicleId);

            Integer gsp = 0;
            Integer hdg = 0;
            Double latitude = 0.0;
            Double longitude = 0.0;

            String fractal = "";
            DBObject result = this.mobileInfo.findOne(queryMongo, fields);
            if (result != null) {
                DBObject pos = (DBObject) result.get("pos");
                gsp = Integer.parseInt(pos.get("gsp").toString());
                hdg = Integer.parseInt(pos.get("hdg").toString());
                DBObject loc = (DBObject) pos.get("loc");
                latitude = Double.parseDouble(loc.get("lat").toString());
                longitude = Double.parseDouble(loc.get("lon").toString());
                fractal = Util.getFractal(latitude, longitude);
            }

            replyMessage.setIntProperty("gsp", gsp);
            replyMessage.setIntProperty("hdg", hdg);
            replyMessage.setDoubleProperty("lat", latitude);
            replyMessage.setDoubleProperty("lon", longitude);
            replyMessage.setStringProperty("fractal", fractal);

        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Issue: IFL-1572
     *
     * @throws UnknownHostException
     */
    private void connectMongoDB() throws UnknownHostException {
        this.mongoDB = MongoDBConnectionManager.instanceFromSystemProperties();
        this.mongoConnection = mongoDB.getConnection();
        this.mongoDatabase = mongoDB.getDB(this.mongoConnection);
        this.hisTrailer = this.mongoDatabase.getCollection("his_trailer");
        this.mobileInfo = this.mongoDatabase.getCollection("mobile_info");
    }

    /**
     * Issue: IFL-1572
     *
     * @throws JMSException
     * @throws NamingException
     */
    private void connectJMS() throws JMSException, NamingException {
        InitialContext ic = new InitialContext();
        ConnectionFactory cf = (ConnectionFactory) ic.lookup("/ConnectionFactory");
        Queue trailerInfoReplyQueue = (Queue) ic.lookup("/queue/TrailerInfoReplyQueue");
        this.jmsConnection = (javax.jms.Connection) cf.createConnection();
        this.jmsSession = (Session) jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        this.producer = jmsSession.createProducer(trailerInfoReplyQueue);
        jmsConnection.start();
    }

    /**
     * Issue: IFL-1572
     *
     * Checks the mongoDB connection
     */
    private void checkMongoDBConnection() {
        if (this.mongoDB == null || this.mongoDB.isAlive(this.mongoConnection) == false) {
            try {
                this.connectMongoDB();
            } catch (UnknownHostException ex) {
                Logger.getLogger(VehicleInfoMDB.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
